using System;
using System.Collections.Generic;
using System.Threading;

namespace EventReactor
{
    public class ActionCountdown
    {
        private int _remaining;
        private Action _complete;
        public ActionCountdown(int count, Action action)
        {
            _remaining = count;
            _complete = action;
        }
        public void Signal()
        {
            if (Interlocked.Decrement(ref _remaining) == 0)
            {
                var action = Interlocked.Exchange(ref _complete, null);
                action?.Invoke();
            }
        }
    }
    /// <summary>
    /// 带有优先级机制的事件，优先级越大顺序越靠前。允许同一优先级有多个回调，按注册顺序执行。
    /// </summary>
    public class PriorityEvent
    {
        private readonly SortedDictionary<int, List<Func<object, IEvent,EndMark>>> _listeners;
        public event Action<Exception> OnThrowException;
        public PriorityEvent()
        {
            _listeners =new(Comparer<int>.Create((x, y) => y.CompareTo(x)));
        }
        public IRemove AddListener(int priority,Func<object, IEvent,EndMark> callback)
        {
            if (!_listeners.TryGetValue(priority, out var existing))
            {
                _listeners[priority] = new List<Func<object, IEvent,EndMark>>();
            }
            _listeners[priority].Add(callback);
            return new Rmv(() =>
            {
                if (_listeners.TryGetValue(priority, out var list))
                {
                    list.Remove(callback);
                    if (list.Count == 0) _listeners.Remove(priority);
                }
            });
        }
        public void SyncInvoke(object sender, ISyncEvent evt, Action<ISyncEvent> onComplete)
        {
            var flattened = new List<Func<object, IEvent, EndMark>>();
            foreach (var kvp in _listeners)
            {
                for (int i = 0; i < kvp.Value.Count; i++)
                {
                    flattened.Add(kvp.Value[i]);
                }
            }
            foreach (var listener in flattened)
            {
                try
                {
                    listener.Invoke(sender, evt);
                }
                catch (Exception e) 
                {
                    OnThrowException(e);
                }
            }
            onComplete?.Invoke(evt);
            onComplete = null;
        }
        public void OrderedAsyncInvoke(object sender, IOrderedAsyncEvent evt, Action<IOrderedAsyncEvent> onComplete)
        {
            var executionQueue = new Queue<Func<object, IEvent, EndMark>>();
            foreach (var kvp in _listeners)
            {
                for (int i = 0; i < kvp.Value.Count; i++)
                {
                    executionQueue.Enqueue(kvp.Value[i]);
                }
            }
            OrderedProcessNext(sender, evt, executionQueue, onComplete);
        }
        private void OrderedProcessNext(object sender, IOrderedAsyncEvent evt, Queue<Func<object, IEvent,EndMark>> _executionQueue, Action<IOrderedAsyncEvent> onComplete)
        {
            evt.EndAct = () => OrderedProcessNext(sender, evt, _executionQueue, onComplete);

            while (_executionQueue.Count>0)
            {
                var current = _executionQueue.Dequeue();
                try
                {
                    var mark = current.Invoke(sender, evt);
                    //只有显式标为回调时才会中断遍历
                    if (mark!=null && mark.IsAsync) return;
                }
                catch (Exception e) 
                {
                    OnThrowException(e);
                }
            }
            //只有处理完毕的分支才能到达这里，不需要再判断
            onComplete?.Invoke(evt);
            onComplete = null;
            _executionQueue = null;
            evt.EndAct = null;
        }
        public void WhenAllAsyncInvoke(object sender, IWhenAllAsyncEvent evt, Action<IWhenAllAsyncEvent> onComplete)
        {
            var flattened = new List<Func<object, IEvent, EndMark>>();
            foreach (var kvp in _listeners)
            {
                for (int i = 0; i < kvp.Value.Count; i++)
                {
                    flattened.Add(kvp.Value[i]);
                }
            }
            var countDown = new ActionCountdown(flattened.Count, () =>
             {
                 onComplete?.Invoke(evt);
                 onComplete = null;
                 evt.EndAct = null;
             });

            evt.EndAct = countDown.Signal;
            foreach (var listener in flattened)
            {
                try
                {
                    var mark = listener.Invoke(sender, evt);
                    if (mark != null && mark.IsAsync) continue;
                }
                catch (Exception e)
                {
                    OnThrowException(e); 
                }
                countDown.Signal();
            }
        }
    }
    public class EventBus:IEventBus
    {
        private readonly Dictionary<Type, PriorityEvent> _evtDic;
        public EventBus()
        {
            _evtDic = new Dictionary<Type, PriorityEvent>();
        }
        private void ThrowException(Exception exception)
        {
            TrySend(this, new OnListenerThrowException(exception), null);
        }
        public IRemove Listen(Type eventType,int priority,Func<object, IEvent,EndMark> callback)
        {
            if (!_evtDic.TryGetValue(eventType, out var evt))
            {
                evt = new PriorityEvent();
                evt.OnThrowException += ThrowException;
                _evtDic[eventType] = evt;
            }
            return evt.AddListener(priority, callback);
        }
        public bool TrySend(object sender, ISyncEvent evt, Action<ISyncEvent> onComplete)
        {
            if (_evtDic.TryGetValue(evt.GetType(), out var unityEvent))
            {
                unityEvent.SyncInvoke(sender, evt, onComplete);
                return true;
            }
            onComplete?.Invoke(evt);
            return false;
        }
        public bool TrySendOrdered(object sender,IOrderedAsyncEvent evt,Action<IOrderedAsyncEvent> onComplete)
        {
            if (_evtDic.TryGetValue(evt.GetType(), out var unityEvent))
            {
                unityEvent.OrderedAsyncInvoke(sender,evt, onComplete);
                return true;
            }
            onComplete?.Invoke(evt);
            return false;
        }
        public bool TrySendWhenAll(object sender, IWhenAllAsyncEvent evt, Action<IWhenAllAsyncEvent> onComplete)
        {
            if (_evtDic.TryGetValue(evt.GetType(), out var unityEvent))
            {
                unityEvent.WhenAllAsyncInvoke(sender, evt, onComplete);
                return true;
            }
            onComplete?.Invoke(evt);
            return false;
        }
    }
    public class Rmv : IRemove
    {
        private Action _act;
        public Rmv() { }
        public Rmv(Action act)
        {
            _act = act;
        }
        public void Add(Action act)
        {
            _act += act;
        }
        public void Remove()
        {
            _act?.Invoke();
            _act = null;
        }
        public static implicit operator Rmv(Action act) => new Rmv(act);
        public static implicit operator Action(Rmv rmv) => rmv._act;
        public static Rmv operator +(Rmv a, IRemove b)
        {
            if (b == null) return a;
            return a?._act + b.Remove;
        }
        public static Rmv operator +(Rmv a, Action b)
        {
            if (b == null) return a;
            return a?._act + b;
        }
    }
    public class OnListenerThrowException:Result
    {
        public Exception exception;
        public OnListenerThrowException(Exception exception)
        {
            this.isSuccess = true;
            this.exception = exception;
        }
    }
}
